#include <list>
#include <iostream>

#define NUM_FLOORS  32
#define FLOOR_STOP_SECS;
#define ELEVATOR_IDLE   0x0
#define ELEVATOR_UP     0x01
#define ELEVATOR_DOWN   0x02
#define ELEVATOR_REPAIR 0xff
// TODO may consider adding SERVING state to
// reflect time in between door OPEN/CLOSE
 
class controller;
class building;

class passenger {
    public:
        passenger(panel *p, int in, int out):
            _panel(p), _in(in), _out(out) {}
        int floor_arrival() { return _in; }
        int floor_exit() { return _out; }

    private:
        panel *_panel; // panel to which passenger aware of
        int _in;    // floor in which passenger arrives
        int _out;   // floor for which passenger exits
        void start();
};

struct request {
    bool value;
    int direc;
};

class elevator {
    public:
        elevator(int fl, int state = NEUTRAL,
            int velocity = DEFAULT_SPEED, int num = 0, building &b):
                _cur_fl(fl), _state(state), _velocity(velocity),
                _num_passengers(num), _door_state(DOOR_CLOSED), _thisBuilding(b) 
                {
                    passengers.clear(); 
                    requests = new (struct request)[b.num_floors()]
                }
        void operate(); // start normal operation
        void schedule_request(int fl, int direc);
        bool has_request(int fl) {return requests[fl].valid; }

    private:
        // state variable
        int _cur_fl;
        int _state;                 // up or down, or idle
        int _velocity;              // moving speed, fls/sec
        int _num_passengers;        // number of passengers
        bool door_state;            // state of the door
        building &_thisBuilding;    // reference to building
        struct request *_requests;  // requests to floors
        // list of passengers inside the elevator
        list<passenger> passengers;
        void move_up() { _state = ELEVATOR_UP; }
        void move_down() { _state = ELEVATOR_DOWN; }
        void open_door() { door_state = DOOR_OPENED; }
        void close_door() { door_state = DOOR_CLOSED; }
        void clear_request(int fl) {requests[fl] = false;}
        void accept_passengers();
        int compute_cost();
};

typedef struct panel {
    bool up;
    bool down;
    // allows a passenger to press the panel of a particular floor
    press(int direction);
} panel_t;

// resource holder
class building {
    public:
        building(int num_f = NUM_FLOORS, int num_e = NUM_ELEVATORS):
            _num_floors(num_f), _num_elevators(num_e) {
                _panels = new panel_t[_num_floors];
                _elevators = new elevator[_num_elevators];
                _fl_queue = new list<passenger>[_num_floors];
            }
        ~building() {
            delete [] _panels;
            delete [] _elevators;
        }
        panel_t *get_panel(int floor) {
            if (floor <= 0 || floor > _num_floors)
                return NULL;
            return _panels + floor - 1;
        }
        int num_floors() {return _num_floors}
        int num_elevators() {return _num_elevators;}
        list<passenger> *_fl_queue; // passengers inside a floor

    private:
        int _num_floors;
        int _num_elevators;
        panel_t *_panels;       // collection of panels on each floor
                                // shabby replacement of "floor"
        elevator *_elevators;   // collection of elevators;
        friend class controller; // controller has power to access building stats
}

class controller {
    public:
        controller(building & b): _thisBuilding(b) {_running = false;}
        void run();
        void stop() {_running = false;}

    private:
        building & _thisBuilding;
        bool _running;
        void update_states();
        void schedule(); 
        bool is_panel_up(int floor) { return _panels[floor].up; }
        bool is_panel_down(int floor) { return _panels[floor].down; }
};


